using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline.Processors;

// TODO: replace these with the processor input and output types.
using TInput = TextListContentPipelineExtension.TextListContent;
using TOutput = TextListContentPipelineExtension.TextListContent;
using System.ComponentModel;

namespace TextListContentPipelineExtension
{
    /// <summary>
    /// This class will be instantiated by the XNA Framework Content Pipeline
    /// to apply custom processing to content data, converting an object of
    /// type TInput to TOutput. The input and output types may be the same if
    /// the processor wishes to alter data without changing its type.
    ///
    /// This should be part of a Content Pipeline Extension Library project.
    ///
    /// As you can see above, we changed TInput and TOutput to be TextListContent. We
    /// could have just specified those types directly below where TInput and TOutput
    /// are positioned. It doesn't really matter which way you do it. Note that
    /// we must have the ContentProcessorAttribute applied to this class. We have set the
    /// DisplayName to "Text List Processor", which is what appears in the properties in
    /// Solution Explorer as the name for this content processor.
    /// 
    /// This is the second stage of a content pipeline extension. Content comes in through
    /// a ContentImporter, which reads it in from the file to the specified class type. Then
    /// that content is sent to the ContentProcess that was specified in the content's
    /// properties in SolutionExplorer. From here it goes to the ContentWriter for the
    /// type of TOutput.
    /// 
    /// We start this class with two processor parameters and then an illustration of
    /// how to hide something that would otherwise appear and an illustration of something
    /// that won't appear even without any hiding done.
    /// 
    /// After that we get to the Process() method, which is where the work is done.
    /// </summary>
    [ContentProcessor(DisplayName = "Text List Processor")]
    public class TextListContentProcessor : ContentProcessor<TInput, TOutput>
    {
        /// <summary>
        /// The backing field for CapitalizeFirstLetter.
        /// </summary>
        private bool capitalizeFirstLetter = true;

        /// <summary>
        /// This is a content processor parameter. When you view the properties of
        /// a processor, you will see these parameters. Note that you should apply
        /// the DisplayNameAttribute, the DefaultValueAttribute and the
        /// DescriptionAttribute in order to ensure that this works as you want it
        /// to and isn't confusing to users. Also, we make this virtual so that if
        /// you write a processor and want it to derive from this, you can override
        /// this property to have it work differently if necessary.
        /// 
        /// This particular parameter is used to indicate whether we should
        /// make the first letter of a line of text a capital letter or leave it
        /// as is.
        /// </summary>
        [DisplayName("Capitalize First Letter")]
        [DefaultValue(true)]
        [Description("Capitalizes the first letter of a line of text when true.")]
        public virtual bool CapitalizeFirstLetter
        {
            get
            {
                return capitalizeFirstLetter;
            }
            set
            {
                capitalizeFirstLetter = value;
            }
        }

        /// <summary>
        /// The backing field for RemoveEmptyLines.
        /// </summary>
        private bool removeEmptyLines = true;

        /// <summary>
        /// This is another parameter. This one indicates whether or not empty lines should be removed.
        /// Note that just because I used two bools doesn't mean you can only use bools. You can use almost
        /// any type really. See the TintTextureContentPipelineExtension for other examples.
        /// </summary>
        [DisplayName("Remove Empty Lines")]
        [DefaultValue(true)]
        [Description("Removes lines that are empty or solely composed of whitespace when true.")]
        public virtual bool RemoveEmptyLines
        {
            get
            {
                return removeEmptyLines;
            }
            set
            {
                removeEmptyLines = value;
            }
        }

        /// <summary>
        /// Any public property you create will, by default, be seen in the content processor settings.
        /// If you want a property to be hidden, apply the BrowsableAttribute and set it to false, as
        /// below.
        /// </summary>
        [Browsable(false)]
        public int YouShouldNotSeeMe
        {
            get;
            set;
        }

        /// <summary>
        /// Since this is a field, not a property, it won't be displayed as a content processor parameter
        /// even though it's public and doesn't have the BrowsableAttribute applied with false.
        /// </summary>
        public int YouAlsoShouldNotSeeMe;




        /// <summary>
        /// Perform any processing on the data you've read in. Here is where you would do things like
        /// write out ExternalReference&lt;T&gt; data such as Texture2DContent if you were doing
        /// processing that required such. This processor removes lines that are blank or entirely
        /// whitespace and then capitalizes the first letter of any other lines.
        /// </summary>
        /// <param name="input">The input from our ContentImporter&lt;T&gt;-derived class.</param>
        /// <param name="context">The Content Pipeline-supplied ContentProcessorContext.</param>
        /// <returns>The processed content ready to be written out as an XNB.</returns>
        public override TOutput Process(TInput input, ContentProcessorContext context)
        {
            // If there's no processing to do, just return the input as is.
            if (!RemoveEmptyLines && !CapitalizeFirstLetter)
            {
                return input;
            }
            
            // We use a reverse order 'for' loop here since we will possibly be removing and reinserting
            // strings based on the processor parameters and you would run into all sort of problems
            // if you used a normal, incrementing 'for' loop that starts at 0.
            for (int i = input.TextData.Count - 1; i >= 0; i--)
            {
                var data = input.TextData[i];

                if (String.IsNullOrWhiteSpace(data))
                {
                    if (RemoveEmptyLines)
                    {
                        context.Logger.LogMessage("Removing blank line.");
                        input.TextData.RemoveAt(i);
                    }
                    continue;
                }

                if (CapitalizeFirstLetter)
                {
                    if (data.Length == 1)
                    {
                        data = data.ToUpper();
                    }
                    else
                    {
                        data = data[0].ToString().ToUpper() + data.Substring(1);
                    }
                    input.TextData.RemoveAt(i);
                    input.TextData.Insert(i, data);
                }
            }

            return input;
        }
    }
}